﻿using System;
using System.Xml;
using System.Security.Principal;
using System.Runtime.InteropServices;

public class ReserveHttpNamespace
{
    private static readonly string KernelWorkflowRunnerConsoleUrlPrefix = "http://+:7777/ServiceHost/UniverSys.svc/";

    static void Main(string[] args)
    {
        if (args.Length != 1)
        {
            Console.WriteLine(
                "Usage: AccessPermitterForUsualUser " +
                "account");
            Console.WriteLine("==>account must be like \"MYLOVELYCOMP\\Student\"");
            return;
        }
        try
        {
            ModifyReservation(KernelWorkflowRunnerConsoleUrlPrefix,
                args[0], false);
            Console.WriteLine("Success!");
        }
        catch (Exception x)
        {
            Console.WriteLine(x.Message);
        }
    }

    static void ModifyReservation(
            string urlPrefix, string accountName,
            bool removeReservation)
    {
        string sddl = createSddl(accountName);
        HTTP_SERVICE_CONFIG_URLACL_SET configInfo;
        configInfo.Key.UrlPrefix = urlPrefix;
        configInfo.Param.Sddl = sddl;
        HTTPAPI_VERSION httpApiVersion =
            new HTTPAPI_VERSION(1, 0);
        int errorCode = HttpInitialize(httpApiVersion,
            HTTP_INITIALIZE_CONFIG, IntPtr.Zero);
        if (0 != errorCode)
            throw getException("HttpInitialize", errorCode);
        try
        {
            // do our best to delete any existing ACL
            errorCode = HttpDeleteServiceConfigurationAcl(
                IntPtr.Zero, HttpServiceConfigUrlAclInfo,
                ref configInfo, Marshal.SizeOf(
                    typeof(HTTP_SERVICE_CONFIG_URLACL_SET)),
                IntPtr.Zero);
            if (removeReservation)
            {
                if (0 != errorCode) throw getException(
                    "HttpDeleteServiceConfigurationAcl",
                    errorCode);
                return;
            }
            errorCode = HttpSetServiceConfigurationAcl(
                IntPtr.Zero, HttpServiceConfigUrlAclInfo,
                ref configInfo, Marshal.SizeOf(
                    typeof(HTTP_SERVICE_CONFIG_URLACL_SET)),
                IntPtr.Zero);
            if (0 != errorCode) throw getException(
                "HttpSetServiceConfigurationAcl", errorCode);
        }
        finally
        {
            errorCode = HttpTerminate(
                HTTP_INITIALIZE_CONFIG, IntPtr.Zero);
            if (0 != errorCode) throw getException(
                "HttpTerminate", errorCode);
        }
    }

    static Exception getException(string fcn, int errorCode)
    {
        return new Exception(
            string.Format("{0} failed: {1}",
            fcn, getWin32ErrorMessage(errorCode)));
    }

    static string createSddl(string account)
    {
        string sid = new NTAccount(account).Translate(
            typeof(SecurityIdentifier)).ToString();
        // DACL that allows generic execute for the user
        // specified by account.
        // See help for HTTP_SERVICE_CONFIG_URLACL_PARAM
        // for details on what this means.
        return string.Format("D:(A;;GX;;;{0})", sid);
    }

    static string getWin32ErrorMessage(int errorCode)
    {
        return Marshal.GetExceptionForHR(HRESULT_FROM_WIN32(errorCode)).ToString();
    }
    static int HRESULT_FROM_WIN32(int errorCode)
    {
        if (errorCode <= 0) return errorCode;
        return (int)((0x0000FFFFU & ((uint)errorCode)) | (7U << 16) |
            0x80000000U);
    }

    // P/Invoke stubs from http.h
    const int HttpServiceConfigUrlAclInfo = 2;
    const int HTTP_INITIALIZE_CONFIG = 2;
    [StructLayout(LayoutKind.Sequential)]
    struct HTTPAPI_VERSION
    {
        public HTTPAPI_VERSION(short maj, short min)
        {
            Major = maj; Minor = min;
        }
        short Major;
        short Minor;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct HTTP_SERVICE_CONFIG_URLACL_KEY
    {
        [MarshalAs(UnmanagedType.LPWStr)]
        public string UrlPrefix;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct HTTP_SERVICE_CONFIG_URLACL_PARAM
    {
        [MarshalAs(UnmanagedType.LPWStr)]
        public string Sddl;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct HTTP_SERVICE_CONFIG_URLACL_SET
    {
        public HTTP_SERVICE_CONFIG_URLACL_KEY Key;
        public HTTP_SERVICE_CONFIG_URLACL_PARAM Param;
    }
    [DllImport("httpapi.dll", ExactSpelling = true,
            EntryPoint = "HttpSetServiceConfiguration")]
    static extern int HttpSetServiceConfigurationAcl(
        IntPtr mustBeZero, int configID,
        [In] ref HTTP_SERVICE_CONFIG_URLACL_SET configInfo,
        int configInfoLength, IntPtr mustBeZero2);
    [DllImport("httpapi.dll", ExactSpelling = true,
            EntryPoint = "HttpDeleteServiceConfiguration")]
    static extern int HttpDeleteServiceConfigurationAcl(
        IntPtr mustBeZero, int configID,
        [In] ref HTTP_SERVICE_CONFIG_URLACL_SET configInfo,
        int configInfoLength, IntPtr mustBeZero2);
    [DllImport("httpapi.dll")]
    static extern int HttpInitialize(
        HTTPAPI_VERSION version,
        int flags, IntPtr mustBeZero);
    [DllImport("httpapi.dll")]
    static extern int HttpTerminate(int flags,
        IntPtr mustBeZero);
}
